package com.me.spider;

import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.core.Point;
import org.opencv.core.Scalar;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;

/**
 * 滑块验证码自动处理：https://mp.weixin.qq.com/s/g4Ihe22Dm_9ncwpzY9V30w
 */
public class TestVerificationCode {

    public static void main(String[] args) throws Exception {
        // 从 原始尺寸的 雪碧图（或拼合图、精灵图）中裁剪出滑块。
        // BufferedImage image = ImageUtils.readBufferImage("C:\\Users\\bin.yin\\Desktop\\img\\sli.png");
        // BufferedImage subImage = ImageUtils.imageCutByRectangle(image, 140, 490, 120, 120, true);

        System.load("E:/Soft/opencv_460/build/java/x64/opencv_java460.dll");

        String backgroundPicPath = "C:\\Users\\bin.yin\\Desktop\\img\\bg.png";
        Mat slideBgMat = preProcessMainImage(backgroundPicPath);
        String slideBlockPicPath = "C:\\Users\\bin.yin\\Desktop\\img\\slider.png";
        Mat slideBlockMat = preProcessSliderImage(slideBlockPicPath);

        // 在原图上计算偏移
        // double sliderDistance222 = new ImageUtils().getDistance(backgroundPicPath, slideBlockPicPath, 0);
        // System.out.println(sliderDistance222);

        System.out.println("============");
        // double sliderDistance = getSliderDistance(Imgcodecs.imread(backgroundPicPath), Imgcodecs.imread(slideBlockPicPath));
        double sliderDistance = getSliderDistance(slideBgMat, slideBlockMat);
        System.out.println(sliderDistance);

        // 获取style属性值，其中设置了滑块初始偏离值  style=left: 23px;
        int left = 23;
        // 网页前端图片和本地图片比例是不同的，需要进行换算：
        // 滑块、缺口背景图 都是等比缩放的，所以直接用缺块背景计算比例即可（原图宽度 672px，html宽度 280px）
        // ratio = 280 / 672
        sliderDistance = getZoomSize(sliderDistance, 672, 280) - left;
        System.out.println(sliderDistance);
    }

    /**
     * 获取缩放后的尺寸
     *
     * @param size              缩放前的尺寸
     * @param originalSize      缩放前的原始尺寸
     * @param targetSize        缩放后的尺寸
     * @return
     */
    public static double getZoomSize(double size, int originalSize, int targetSize) {
        return size * targetSize / originalSize;
    }

    /**
     * 根据缩放后的尺寸计算原始尺寸
     * @param size
     * @param originalSize
     * @param targetSize
     * @return
     */
    public static double resetSize(double size, int originalSize, int targetSize) {
        return size * originalSize / targetSize;
    }

    /**
     * 获取滑块背景缺口图到左边框的横向距离
     *
     * @param slideBgMat        有缺口的背景图
     * @param slideBlockMat     滑块
     * @return
     */
    public static double getSliderDistance(Mat slideBgMat, Mat slideBlockMat) {
        Mat g_result = new Mat();
        /*
         * matchTemplate：在模板和输入图像之间寻找匹配,获得匹配结果图像
         * result：保存匹配的结果矩阵
         * TM_CCOEFF_NORMED标准相关匹配算法
         */
        Imgproc.matchTemplate(slideBgMat, slideBlockMat, g_result, Imgproc.TM_CCOEFF_NORMED);
        /*
         * minMaxLoc：在给定的结果矩阵中寻找最大和最小值，并给出它们的位置
         * maxLoc最大值
         */
        Point matchLocation = Core.minMaxLoc(g_result).maxLoc;
        // System.out.println(matchLocation);

        // 在原图上标记匹配点
        signRectangle(slideBlockMat, Core.minMaxLoc(g_result));

        // 返回匹配点的横向距离
        return matchLocation.x;
    }

    /**
     * 在原图上用长方形标记匹配的位置
     *
     * @param slideBlockMat     待匹配的模板图
     * @param locResult         匹配结果
     */
    private static void signRectangle(Mat slideBlockMat, Core.MinMaxLocResult locResult) {
        // 读取原始图片
        Mat backgroundMat = Imgcodecs.imread("C:\\Users\\bin.yin\\Desktop\\img\\bg.png");
        Imgproc.rectangle(backgroundMat, locResult.maxLoc, new Point(locResult.maxLoc.x + slideBlockMat.cols(), locResult.maxLoc.y + slideBlockMat.rows()),
                new Scalar(0, 255, 0, 0));
        Imgproc.rectangle(backgroundMat, locResult.minLoc, new Point(locResult.minLoc.x + slideBlockMat.cols(), locResult.minLoc.y + slideBlockMat.rows()),
                new Scalar(0, 255, 0, 0));
        Imgcodecs.imwrite("C:\\Users\\bin.yin\\Desktop\\img\\bg111.png", backgroundMat);
    }

    /**
     * 预处理背景图片
     *
     * @param backgroundPicPath 待填充的背景图片
     * @return
     */
    public static Mat preProcessMainImage(String backgroundPicPath) {
        // 对滑动背景图进行处理
        Mat backgroundMat = Imgcodecs.imread(backgroundPicPath);
        // 1、灰度化图片
        Imgproc.cvtColor(backgroundMat, backgroundMat, Imgproc.COLOR_BGR2GRAY);
        // 2、二值化
        Imgproc.threshold(backgroundMat, backgroundMat, 127, 255, Imgproc.THRESH_BINARY);
        return backgroundMat;
    }

    public static Mat preProcessMainImage2(String backgroundPicPath) {
        // 对滑动背景图进行处理
        Mat backgroundMat = Imgcodecs.imread(backgroundPicPath);

        // 1、灰度化图片
        Mat newMat = new Mat();
        Imgproc.cvtColor(backgroundMat, newMat, Imgproc.COLOR_BGR2GRAY);
        Imgcodecs.imwrite("C:\\Users\\bin.yin\\Desktop\\img\\bg2.png", newMat);

        // 2、二值化
        Mat newMat2 = new Mat();
        Imgproc.threshold(newMat, newMat2, 127, 255, Imgproc.THRESH_BINARY);
        Imgcodecs.imwrite("C:\\Users\\bin.yin\\Desktop\\img\\bg3.png", newMat2);

        return newMat2;
    }

    /**
     * 预处理滑块图片
     *
     * @param slideBlockPicPath 滑块图片本地路径
     * @return 处理之后的图片内容
     */
    public static Mat preProcessSliderImage(String slideBlockPicPath) {
        // 加载滑块图片
        Mat slideBlockMat = Imgcodecs.imread(slideBlockPicPath);

        // 1、灰度化图片
        Mat newMat = new Mat();
        Imgproc.cvtColor(slideBlockMat, newMat, Imgproc.COLOR_BGR2GRAY);
        // Imgcodecs.imwrite("C:\\Users\\bin.yin\\Desktop\\img\\slider2.png", newMat);

        // 2、去除周围黑边
        for (int row = 0; row < newMat.height(); row++) {
            for (int col = 0; col < newMat.width(); col++) {
                if (newMat.get(row, col)[0] == 0) {
                    newMat.put(row, col, 96);
                }
            }
        }
        // 3、inRange二值化转黑白图
        Mat newMat2 = new Mat();
        // Core.inRange(newMat, Scalar.all(96), Scalar.all(96), newMat2);
        Imgproc.threshold(newMat, newMat2, 127, 255, Imgproc.THRESH_BINARY);
        // Imgcodecs.imwrite("C:\\Users\\bin.yin\\Desktop\\img\\slider3.png", newMat2);

        return newMat2;
    }

}
